#ifndef MPI_H
#define MPI_H

/** \file mpi.h
 *  \brief File which defines all of the MPI functions, constants and datatypes
 */

/** return codes */
/** \def MPI_SUCESS return code*/
#define MPI_SUCCESS				0
/** \def Buffer error return code*/
#define MPI_ERR_BUFFER			1
/** \def returns error count*/
#define MPI_ERR_COUNT			2
/** \def returns error type*/
#define MPI_ERR_TYPE			3
#define MPI_ERR_TAG				4
#define MPI_ERR_COMM			5
#define MPI_ERR_RANK			6
#define MPI_ERR_REQUEST			7
#define MPI_ERR_ROOT			8
#define MPI_ERR_GROUP			9
#define MPI_ERR_OP				10
#define MPI_ERR_TOPOLOGY		11
#define MPI_ERR_DIMS			12
#define MPI_ERR_ARG				13
#define MPI_ERR_UNKNOWN			14
#define MPI_ERR_TRUNCATE		15
#define MPI_ERR_OTHER			16
#define MPI_ERR_INTERN			17
#define MPI_PENDING				18
#define MPI_ERR_IN_STATUS		19
#define MPI_ERR_LASTCODE		20

/** assorted constants */

#define MPI_BOTTOM				(void *)0
#define MPI_PROC_NULL			(-1)
#define MPI_ANY_SOURCE			(-2)
#define MPI_ROOT				(-3)
#define MPI_ANY_TAG				(-1)
#define MPI_UNDEFINED			(-32766)
#define MPI_BSEND_OVERHEAD		512
#define MPI_KEYVAL_INVALID		0
#define MPI_STATUS_IGNORE		(MPI_Status *)1

/** error-handling specifiers */

#define MPI_ERRORS_ARE_FATAL	0
#define MPI_ERRORS_RETURN		1

/** maximum sizes for strings */

#define MPI_MAX_PROCESSOR_NAME	32
#define MPI_MAX_ERROR_STRING	50

/** elementary datatypes */

#define MPI_CHAR				((MPI_Datatype) 1)
#define MPI_SHORT				((MPI_Datatype) 2)
#define MPI_INT					((MPI_Datatype) 3)
#define MPI_LONG				((MPI_Datatype) 4)
#define MPI_UNSIGNED_CHAR		((MPI_Datatype) 5)
#define MPI_UNSIGNED_SHORT		((MPI_Datatype) 6)
#define MPI_UNSIGNED			((MPI_Datatype) 7)
#define MPI_UNSIGNED_LONG		((MPI_Datatype) 8)
#define MPI_FLOAT				((MPI_Datatype) 9)
#define MPI_DOUBLE				((MPI_Datatype) 10)
#define MPI_LONG_DOUBLE			((MPI_Datatype) 11)
#define MPI_BYTE				((MPI_Datatype) 12)
#define MPI_PACKED				((MPI_Datatype) 13)

/** optional datatypes */

#define MPI_LONG_LONG_INT		((MPI_Datatype) 14)

/** special datatypes for constructing derived datatypes */

#define MPI_UB					((MPI_Datatype) 15)
#define MPI_LB					((MPI_Datatype) 16)

/** datatypes for reduction functions */

#define MPI_FLOAT_INT			((MPI_Datatype) 17)
#define MPI_DOUBLE_INT			((MPI_Datatype) 18)
#define MPI_LONG_INT			((MPI_Datatype) 19)
#define MPI_2INT				((MPI_Datatype) 20)
#define MPI_SHORT_INT			((MPI_Datatype) 21)
#define MPI_LONG_DOUBLE_INT		((MPI_Datatype) 22)

/* the following integer is a placeholder for the
 * maximum basic type -- should always be greater
 * than all basic types.  Arbitrarily, 32 for now */
#define MPI_DATATYPE_BASIC_MAX  32

/** reserved communicators */

#define MPI_COMM_WORLD			((MPI_Comm) 0)
#define MPI_COMM_SELF			((MPI_Comm) 1)

/** results of communicator and group comparisons */

#define MPI_IDENT				0
#define MPI_CONGRUENT			1
#define MPI_SIMILAR				2
#define MPI_UNEQUAL				3

/** environment inquiry keys */

#define MPI_TAG_UB				1
#define MPI_IO					2
#define MPI_HOST				3
#define MPI_WTIME_IS_GLOBAL		4

/** collective operations */

#define MPI_MAX					((MPI_Op) 0)
#define MPI_MIN					((MPI_Op) 1)
#define MPI_SUM					((MPI_Op) 2)
#define MPI_PROD				((MPI_Op) 3)
#define MPI_MAXLOC				((MPI_Op) 4)
#define MPI_MINLOC				((MPI_Op) 5)
#define MPI_BAND				((MPI_Op) 6)
#define MPI_BOR					((MPI_Op) 7)
#define MPI_BXOR				((MPI_Op) 8)	
#define MPI_LAND				((MPI_Op) 9)	
#define MPI_LOR					((MPI_Op) 10)
#define MPI_LXOR				((MPI_Op) 11)

/** null handles */

#define MPI_GROUP_NULL			((MPI_Group) -1)
#define MPI_COMM_NULL			((MPI_Comm) -1)
#define MPI_DATATYPE_NULL		((MPI_Datatype) -1)
#define MPI_REQUEST_NULL		((MPI_Request) -1)
#define MPI_OP_NULL				((MPI_Op) -1)
#define MPI_ERRHANDLER_NULL		((MPI_Errhandler) -1)

/** empty group */

#define MPI_GROUP_EMPTY			((MPI_Group) 1)

/** topologies */

#define MPI_GRAPH				1
#define MPI_CART				2

/** opaque types */

typedef int MPI_Aint;
typedef struct {
	int bytes_recvd;
	int MPI_SOURCE;
	int MPI_TAG;
	int MPI_ERROR;
} MPI_Status;

/** handles to assorted structures */

typedef int MPI_Group;
typedef int MPI_Comm;
typedef int MPI_Datatype;
typedef int MPI_Request;
typedef int MPI_Op;
typedef int MPI_Errhandler;

/** prototypes for user-defined functions */

typedef int MPI_Copy_function(MPI_Comm oldcomm, int keyval,
	void *extra_state, void *attribute_val_in,
	void *attribute_val_out, int *flag);
typedef int MPI_Delete_function(MPI_Comm comm, int keyval,
	void *attribute_val, void *extra_state);
typedef void MPI_Handler_function(MPI_Comm *, int *, ...);
typedef void MPI_User_function(void *invec, void *inoutvec, int *len,
	MPI_Datatype *datatype);

/** point-to-point communications */

int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm);
int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source,
	int tag, MPI_Comm comm, MPI_Status *status);
int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count);
int MPI_Bsend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm);
int MPI_Ssend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm);
int MPI_Rsend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm);
int MPI_Buffer_attach(void *buffer, int size);
int MPI_Buffer_detach(void *buffer, int *size);
int MPI_Isend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Ibsend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Issend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Irsend(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Irecv(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Wait(MPI_Request *request, MPI_Status *status);
int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);
int MPI_Request_free(MPI_Request *request);
int MPI_Waitany(int count, MPI_Request *array_of_requests, int *index,
	MPI_Status *status);
int MPI_Testany(int count, MPI_Request *array_of_requests, int *index,
	int *flag, MPI_Status *status);
int MPI_Waitall(int count, MPI_Request *array_of_requests,
	MPI_Status *array_of_statuses);
int MPI_Testall(int count, MPI_Request *array_of_requests, int *flag,
	MPI_Status *array_of_statuses);
int MPI_Waitsome(int incount, MPI_Request *array_of_requests,
	int *outcount, int *array_of_indices, MPI_Status *array_of_statuses);
int MPI_Testsome(int incount, MPI_Request *array_of_requests,
	int *outcount, int *array_of_indices, MPI_Status *array_of_statuses);
int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag,
	MPI_Status *status);
int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status);
int MPI_Cancel(MPI_Request *request);
int MPI_Test_cancelled(MPI_Status *status, int *flag);
int MPI_Send_init(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Bsend_init(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Ssend_init(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Rsend_init(void *buf, int count, MPI_Datatype datatype, int dest,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Recv_init(void *buf, int count, MPI_Datatype datatype, int source,
	int tag, MPI_Comm comm, MPI_Request *request);
int MPI_Start(MPI_Request *request);
int MPI_Startall(int num_requests, MPI_Request *array_of_requests);
int MPI_Sendrecv(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	int dest, int sendtag, void *recvbuf, int recvcount,
	MPI_Datatype recvtype, int source, int recvtag, MPI_Comm comm,
	MPI_Status *status);
int MPI_Sendrecv_replace(void *buf, int count, MPI_Datatype datatype,
	int dest, int sendtag, int source, int recvtag, MPI_Comm comm,
	MPI_Status *status);
	
/** datatype definition and packing/unpacking */

int MPI_Type_contiguous(int count, MPI_Datatype oldtype,
	MPI_Datatype *newtype);
int MPI_Type_vector(int count, int blocklength, int stride,
	MPI_Datatype oldtype, MPI_Datatype *newtype);
int MPI_Type_hvector(int count, int blocklength, MPI_Aint stride,
	MPI_Datatype oldtype, MPI_Datatype *newtype);
int MPI_Type_indexed(int count, int *array_of_blocklengths,
	int *array_of_displacements, MPI_Datatype oldtype, MPI_Datatype *newtype);
int MPI_Type_hindexed(int count, int *array_of_blocklengths,
	MPI_Aint *array_of_displacements, MPI_Datatype oldtype,
	MPI_Datatype *newtype);
int MPI_Type_struct(int count, int *array_of_blocklengths,
	MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types,
	MPI_Datatype *newtype);
int MPI_Address(void *location, MPI_Aint *address);
int MPI_Type_extent(MPI_Datatype datatype, MPI_Aint *extent);
int MPI_Type_size(MPI_Datatype datatype, int *size);
int MPI_Type_lb(MPI_Datatype datatype, MPI_Aint *displacement);
int MPI_Type_ub(MPI_Datatype datatype, MPI_Aint *displacement);
int MPI_Type_commit(MPI_Datatype *datatype);
int MPI_Type_free(MPI_Datatype *datatype);
int MPI_Get_elements(MPI_Status *status, MPI_Datatype datatype, int *count);
int MPI_Pack(void *inbuf, int incount, MPI_Datatype datatype, void *outbuf,
	int outsize, int *position,  MPI_Comm comm);
int MPI_Unpack(void *inbuf, int insize, int *position, void *outbuf,
	int outcount, MPI_Datatype datatype, MPI_Comm comm);
int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm,
	int *size);

/** collective communication */

int MPI_Barrier(MPI_Comm comm);
int MPI_Bcast(void *buf, int count, MPI_Datatype datatype, int root,
	MPI_Comm comm);
int MPI_Gather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
	MPI_Comm comm); 
int MPI_Gatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int *recvcounts, int *displs, MPI_Datatype recvtype,
	int root, MPI_Comm comm); 
int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int recvcount, MPI_Datatype recvtype, int root,
	MPI_Comm comm);
int MPI_Scatterv(void *sendbuf, int *sendcounts, int *displs,
	MPI_Datatype sendtype, void *recvbuf, int recvcount,
	MPI_Datatype recvtype, int root, MPI_Comm comm);
int MPI_Allgather(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm);
int MPI_Allgatherv(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int *recvcounts, int *displs, MPI_Datatype recvtype,
	MPI_Comm comm);
int MPI_Alltoall(void *sendbuf, int sendcount, MPI_Datatype sendtype,
	void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm);
int MPI_Alltoallv(void *sendbuf, int *sendcounts, int *sdispls,
	MPI_Datatype sendtype, void *recvbuf, int *recvcounts, int *rdispls,
	MPI_Datatype recvtype, MPI_Comm comm);
int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype,
	MPI_Op op, int root, MPI_Comm comm);
int MPI_Op_create(MPI_User_function *function, int commute, MPI_Op *op);
int MPI_Op_free(MPI_Op *op);
int MPI_Allreduce(void *sendbuf, void *recvbuf, int count,
	MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
int MPI_Reduce_scatter(void *sendbuf, void *recvbuf, int *recvcounts,
	MPI_Datatype datatype, MPI_Op op, MPI_Comm comm);
int MPI_Scan(void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype,
	MPI_Op op, MPI_Comm comm);

/** groups, contexts and communicators */

int MPI_Group_size(MPI_Group group, int *size);
int MPI_Group_rank(MPI_Group group, int *rank);
int MPI_Group_translate_ranks (MPI_Group group1, int n, int *ranks1,
	MPI_Group group2, int *ranks2);
int MPI_Group_compare(MPI_Group group1, MPI_Group group2, int *result);
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group);
int MPI_Group_union(MPI_Group group1, MPI_Group group2, MPI_Group *newgroup);
int MPI_Group_intersection(MPI_Group group1, MPI_Group group2,
	MPI_Group *newgroup);
int MPI_Group_difference(MPI_Group group1, MPI_Group group2,
	MPI_Group *newgroup);
int MPI_Group_incl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup);
int MPI_Group_excl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup);
int MPI_Group_range_incl(MPI_Group group, int n, int ranks[][3],
	MPI_Group *newgroup);
int MPI_Group_range_excl(MPI_Group group, int n, int ranks[][3],
	MPI_Group *newgroup);
int MPI_Group_free(MPI_Group *group);
int MPI_Comm_size(MPI_Comm comm, int *size);
int MPI_Comm_rank(MPI_Comm comm, int *rank);
int MPI_Comm_compare(MPI_Comm comm1, MPI_Comm comm2, int *result);
int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm);
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm);
int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm);
int MPI_Comm_free(MPI_Comm *comm);
int MPI_Comm_test_inter(MPI_Comm comm, int *flag);
int MPI_Comm_remote_size(MPI_Comm comm, int *size);
int MPI_Comm_remote_group(MPI_Comm comm, MPI_Group *group);
int MPI_Intercomm_create(MPI_Comm intercomm, int local_leader,
	MPI_Comm peer_comm, int remote_leader, int tag, MPI_Comm *comm_out);
int MPI_Intercomm_merge(MPI_Comm intercomm, int high, MPI_Comm *comm_out);
int MPI_Keyval_create(MPI_Copy_function *copy_fn,
	MPI_Delete_function *delete_fn, int *keyval, void *extra_state);
int MPI_Keyval_free(int *keyval);
int MPI_Attr_put(MPI_Comm comm, int keyval, void *attribute_val);
int MPI_Attr_get(MPI_Comm comm, int keyval, void *attribute_val, int *flag);
int MPI_Attr_delete(MPI_Comm comm, int keyval);

/** process topologies */

int MPI_Topo_test(MPI_Comm comm, int *status);
int MPI_Cart_create(MPI_Comm comm_old, int ndims, int *dims, int *periods,
	int reorder, MPI_Comm *comm_cart);
int MPI_Dims_create(int nnodes, int ndims, int *dims);
int MPI_Graph_create(MPI_Comm comm_old, int nnodes, int *index, int *edges,
	int reorder, MPI_Comm *comm_cart);
int MPI_Graphdims_get(MPI_Comm comm, int *nnodes, int *nedges);
int MPI_Graph_get(MPI_Comm comm, int maxindex, int maxedges, int *index,
	int *edges);
int MPI_Cartdim_get(MPI_Comm comm, int *ndims);
int MPI_Cart_get(MPI_Comm comm, int maxdims, int *dims, int *periods,
	int *coords);
int MPI_Cart_rank(MPI_Comm comm, int *coords, int *rank);
int MPI_Cart_coords(MPI_Comm comm, int rank, int maxdims, int *coords);
int MPI_Graph_neighbors_count(MPI_Comm comm, int rank, int *nneighbors);
int MPI_Graph_neighbors(MPI_Comm comm, int rank, int maxneighbors,
	int *neighbors);
int MPI_Cart_shift(MPI_Comm comm, int direction, int disp, int *rank_source,
	int *rank_dest);
int MPI_Cart_sub(MPI_Comm comm, int *remain_dims, MPI_Comm *newcomm);
int MPI_Cart_map(MPI_Comm comm, int ndims, int *dims, int *periods,
	int *newrank);
int MPI_Graph_map(MPI_Comm comm, int nnodes, int *index, int *edges,
	int *newrank);

/** environmental inquiriy */

int MPI_Get_processor_name(char *name, int *resultlen);
int MPI_Errhandler_create(MPI_Handler_function *function,
	MPI_Errhandler *errhandler);
int MPI_Errhandler_set(MPI_Comm comm, MPI_Errhandler errhandler);
int MPI_Errhandler_get(MPI_Comm comm, MPI_Errhandler *errhandler);
int MPI_Errhandler_free(MPI_Errhandler *errhandler);
int MPI_Error_string(int errorcode, char *string, int *resultlen);
int MPI_Error_class(int errorcode, int *errorclass);
double MPI_Wtime(void);
double MPI_Wtick(void);
int MPI_Init(int *argc, char ***argv);
int MPI_Finalize(void);
int MPI_Initialized(int *flag);
int MPI_Abort(MPI_Comm comm, int errorcode);
int MPI_Get_version(int *version, int *subversion);

/** profiling */

int MPI_Pcontrol(const int level, ...);


#endif
